#include "smart_mpp_encoder.hpp"
#include <vector>
#include <cstdint>

int SmartMppEncoder::init(enc_para_t& enc_para)
{
    int ret;
    MppPollType timeout = MPP_POLL_BLOCK;

    init_enc_para(enc_para);

    ret = mpp_buffer_group_get_internal(&this->m_buf_grp, MPP_BUFFER_TYPE_DRM);
    if (ret) {
        log_debug("failed to get mpp buffer group ret %d\n", ret);
        exit(EXIT_FAILURE);
    }

    log_debug("m_buf_grp:%p m_frm_buf:%p m_frame_size:%d m_header_size:%d.", m_buf_grp, m_frm_buf, m_frame_size, m_header_size);
    ret = mpp_buffer_get(this->m_buf_grp, &this->m_frm_buf, (this->m_frame_size + this->m_header_size));
    if (ret) {
        log_debug("failed to get buffer for input frame ret %d\n", ret);
        exit(EXIT_FAILURE);
    }

    log_debug("m_buf_grp:%p m_pkt_buf:%p m_frame_size:%d.", m_buf_grp, m_frm_buf, m_frame_size);
    ret = mpp_buffer_get(this->m_buf_grp, &this->m_pkt_buf, this->m_frame_size);
    if (ret) {
        log_debug("failed to get buffer for output packet ret %d\n", ret);
        exit(EXIT_FAILURE);
    }

    log_debug("m_buf_grp:%p m_md_info:%p m_mdinfo_size:%d.", m_buf_grp, m_md_info, m_mdinfo_size);
    ret = mpp_buffer_get(this->m_buf_grp, &this->m_md_info, this->m_mdinfo_size);
    if (ret) {
        log_debug("failed to get buffer for motion info output packet ret %d\n", ret);
        exit(EXIT_FAILURE);
    }

    // encoder demo
    ret = mpp_create(&this->m_mpp_ctx, &this->m_mpp_mpi);
    if (ret) {
        log_error("mpp_create failed ret %d\n", ret);
        exit(EXIT_FAILURE);
    }

    log_debug("%p encoder test start w %d h %d type %d\n",
              this->m_mpp_ctx, m_enc_para.width, m_enc_para.height, m_enc_para.type);

    ret = m_mpp_mpi->control(m_mpp_ctx, MPP_SET_OUTPUT_TIMEOUT, &timeout);
    if (MPP_OK != ret) {
        log_error("mpi control set output timeout %d ret %d\n", timeout, ret);
        exit(EXIT_FAILURE);
    }

    ret = mpp_init(m_mpp_ctx, MPP_CTX_ENC, m_enc_para.type);
    if (ret) {
        log_error("mpp_init failed ret %d\n", ret);
        exit(EXIT_FAILURE);
    }

    this->setup_enc_cfg();
    return 0;
}

int SmartMppEncoder::deinit()
{
    if (this->m_mpp_ctx) {
        mpp_destroy(this->m_mpp_ctx);
        this->m_mpp_ctx = NULL;
    }

    if (this->m_cfg) {
        mpp_enc_cfg_deinit(this->m_cfg);
        this->m_cfg = NULL;
    }

    if (this->m_frm_buf) {
        mpp_buffer_put(this->m_frm_buf);
        this->m_frm_buf = NULL;
    }

    if (this->m_pkt_buf) {
        mpp_buffer_put(this->m_pkt_buf);
        this->m_pkt_buf = NULL;
    }

    if (this->m_md_info) {
        mpp_buffer_put(this->m_md_info);
        this->m_md_info = NULL;
    }

    // if (this->osd_data.buf) {
    //     mpp_buffer_put(this->osd_data.buf);
    //     this->osd_data.buf = NULL;
    // }

    if (this->m_buf_grp) {
        mpp_buffer_group_put(this->m_buf_grp);
        this->m_buf_grp = NULL;
    }

    return 0;
}

static int mpi_enc_width_default_stride(int width, MppFrameFormat fmt)
{
    int stride = 0;

    switch (fmt & MPP_FRAME_FMT_MASK) {
    case MPP_FMT_YUV420SP :
    case MPP_FMT_YUV420SP_VU : {
        stride = MPP_ALIGN(width, 8);
    } break;
    case MPP_FMT_YUV420P : {
        /* NOTE: 420P need to align to 16 so chroma can align to 8 */
        stride = MPP_ALIGN(width, 16);
    } break;
    case MPP_FMT_YUV422P:
    case MPP_FMT_YUV422SP:
    case MPP_FMT_YUV422SP_VU: {
        /* NOTE: 422 need to align to 8 so chroma can align to 16 */
        stride = MPP_ALIGN(width, 8);
    } break;
    case MPP_FMT_YUV444SP :
    case MPP_FMT_YUV444P : {
        stride = MPP_ALIGN(width, 8);
    } break;
    case MPP_FMT_RGB565:
    case MPP_FMT_BGR565:
    case MPP_FMT_RGB555:
    case MPP_FMT_BGR555:
    case MPP_FMT_RGB444:
    case MPP_FMT_BGR444:
    case MPP_FMT_YUV422_YUYV :
    case MPP_FMT_YUV422_YVYU :
    case MPP_FMT_YUV422_UYVY :
    case MPP_FMT_YUV422_VYUY : {
        /* NOTE: for vepu limitation */
        stride = MPP_ALIGN(width, 8) * 2;
    } break;
    case MPP_FMT_RGB888 :
    case MPP_FMT_BGR888 : {
        /* NOTE: for vepu limitation */
        stride = MPP_ALIGN(width, 8) * 3;
    } break;
    case MPP_FMT_RGB101010 :
    case MPP_FMT_BGR101010 :
    case MPP_FMT_ARGB8888 :
    case MPP_FMT_ABGR8888 :
    case MPP_FMT_BGRA8888 :
    case MPP_FMT_RGBA8888 : {
        /* NOTE: for vepu limitation */
        stride = MPP_ALIGN(width, 8) * 4;
    } break;
    default : {
        mpp_err_f("do not support type %d\n", fmt);
    } break;
    }

    return stride;
}

int SmartMppEncoder::init_enc_para(enc_para_t& enc_para)
{
    memcpy(&m_enc_para, &enc_para, sizeof(enc_para_t));

    // 配置默认参数
    if (m_enc_para.hor_stride == 0) {  // 计算水平步幅
        // m_enc_para.hor_stride = MPP_ALIGN(m_enc_para.width, 16);
        m_enc_para.hor_stride = mpi_enc_width_default_stride(m_enc_para.width, enc_para.fmt);
    }
    if (m_enc_para.ver_stride == 0) {  // 计算垂直步幅
        m_enc_para.ver_stride = (MPP_ALIGN(m_enc_para.height, 16));
    }

    if (m_enc_para.fps_in_den == 0)
        m_enc_para.fps_in_den = 1;
    if (m_enc_para.fps_in_num == 0)
        m_enc_para.fps_in_num = 30;
    if (m_enc_para.fps_out_den == 0)
        m_enc_para.fps_out_den = 1;
    if (m_enc_para.fps_out_num == 0)
        m_enc_para.fps_out_num = 30;

    if (!m_enc_para.bps)
        m_enc_para.bps = m_enc_para.width * m_enc_para.height / 8 * (m_enc_para.fps_out_num / m_enc_para.fps_out_den);

    this->m_mdinfo_size  = (MPP_VIDEO_CodingHEVC == m_enc_para.type) ?
                      (MPP_ALIGN(m_enc_para.hor_stride, 32) >> 5) *
                      (MPP_ALIGN(m_enc_para.ver_stride, 32) >> 5) * 16 :
                      (MPP_ALIGN(m_enc_para.hor_stride, 64) >> 6) *
                      (MPP_ALIGN(m_enc_para.ver_stride, 16) >> 4) * 16;

    // 更新资源参数
    switch (m_enc_para.fmt & MPP_FRAME_FMT_MASK) {
    case MPP_FMT_YUV420SP:
    case MPP_FMT_YUV420P: {
        this->m_frame_size = MPP_ALIGN(m_enc_para.hor_stride, 64) * MPP_ALIGN(m_enc_para.ver_stride, 64) * 3 / 2;
    } break;

    case MPP_FMT_YUV422_YUYV :
    case MPP_FMT_YUV422_YVYU :
    case MPP_FMT_YUV422_UYVY :
    case MPP_FMT_YUV422_VYUY :
    case MPP_FMT_YUV422P :
    case MPP_FMT_YUV422SP : {
        this->m_frame_size = MPP_ALIGN(m_enc_para.hor_stride, 64) * MPP_ALIGN(m_enc_para.ver_stride, 64) * 2;
    } break;
    case MPP_FMT_RGB444 :
    case MPP_FMT_BGR444 :
    case MPP_FMT_RGB555 :
    case MPP_FMT_BGR555 :
    case MPP_FMT_RGB565 :
    case MPP_FMT_BGR565 :
    case MPP_FMT_RGB888 :
    case MPP_FMT_BGR888 :
    case MPP_FMT_RGB101010 :
    case MPP_FMT_BGR101010 :
    case MPP_FMT_ARGB8888 :
    case MPP_FMT_ABGR8888 :
    case MPP_FMT_BGRA8888 :
    case MPP_FMT_RGBA8888 : {
        this->m_frame_size = MPP_ALIGN(m_enc_para.hor_stride, 64) * MPP_ALIGN(m_enc_para.ver_stride, 64);
    } break;

    default: {
        this->m_frame_size = MPP_ALIGN(m_enc_para.hor_stride, 64) * MPP_ALIGN(m_enc_para.ver_stride, 64) * 4;
    } break;
    }

    if (MPP_FRAME_FMT_IS_FBC(m_enc_para.fmt)) {  // FBC 帧缓冲压缩格式
        if ((m_enc_para.fmt & MPP_FRAME_FBC_MASK) == MPP_FRAME_FBC_AFBC_V1)
            this->m_header_size = MPP_ALIGN(MPP_ALIGN(m_enc_para.width, 16) * MPP_ALIGN(m_enc_para.height, 16) / 16, SZ_4K);
        else
            this->m_header_size = MPP_ALIGN(m_enc_para.width, 16) * MPP_ALIGN(m_enc_para.height, 16) / 16;
    } else {
        this->m_header_size = 0;
    }

    return 0;
}

int SmartMppEncoder::setup_enc_cfg()
{
    int ret;

    ret = mpp_enc_cfg_init(&m_cfg);
    if (ret) {
        log_error("mpp_enc_cfg_init failed ret %d\n", ret);
        exit(EXIT_FAILURE);
    }

    ret = m_mpp_mpi->control(m_mpp_ctx, MPP_ENC_GET_CFG, m_cfg);
    if (ret) {
        log_error("get enc cfg failed ret %d\n", ret);
        exit(EXIT_FAILURE);
    }

    /* setup default parameter */
    if (m_enc_para.fps_in_den == 0)
        m_enc_para.fps_in_den = 1;
    if (m_enc_para.fps_in_num == 0)
        m_enc_para.fps_in_num = 30;
    if (m_enc_para.fps_out_den == 0)
        m_enc_para.fps_out_den = 1;
    if (m_enc_para.fps_out_num == 0)
        m_enc_para.fps_out_num = 30;

    if (!m_enc_para.bps)
        m_enc_para.bps = m_enc_para.width * m_enc_para.height / 8 * (m_enc_para.fps_out_num / m_enc_para.fps_out_den);

    mpp_enc_cfg_set_s32(m_cfg, "prep:width", m_enc_para.width);
    mpp_enc_cfg_set_s32(m_cfg, "prep:height", m_enc_para.height);
    mpp_enc_cfg_set_s32(m_cfg, "prep:hor_stride", m_enc_para.hor_stride);
    mpp_enc_cfg_set_s32(m_cfg, "prep:ver_stride", m_enc_para.ver_stride);
    mpp_enc_cfg_set_s32(m_cfg, "prep:format", m_enc_para.fmt);

    mpp_enc_cfg_set_s32(m_cfg, "rc:mode", m_enc_para.rc_mode);

    /* fix input / output frame rate */
    mpp_enc_cfg_set_s32(m_cfg, "rc:fps_in_flex", m_enc_para.fps_in_flex);
    mpp_enc_cfg_set_s32(m_cfg, "rc:fps_in_num", m_enc_para.fps_in_num);
    mpp_enc_cfg_set_s32(m_cfg, "rc:fps_in_denorm", m_enc_para.fps_in_den);
    mpp_enc_cfg_set_s32(m_cfg, "rc:fps_out_flex", m_enc_para.fps_out_flex);
    mpp_enc_cfg_set_s32(m_cfg, "rc:fps_out_num", m_enc_para.fps_out_num);
    mpp_enc_cfg_set_s32(m_cfg, "rc:fps_out_denorm", m_enc_para.fps_out_den);
    mpp_enc_cfg_set_s32(m_cfg, "rc:gop", m_enc_para.gop_len ? m_enc_para.gop_len : m_enc_para.fps_out_num * 2);

    /* drop frame or not when bitrate overflow */
    mpp_enc_cfg_set_u32(m_cfg, "rc:drop_mode", MPP_ENC_RC_DROP_FRM_DISABLED);
    mpp_enc_cfg_set_u32(m_cfg, "rc:drop_thd", 20);        /* 20% of max bps */
    mpp_enc_cfg_set_u32(m_cfg, "rc:drop_gap", 1);         /* Do not continuous drop frame */

    /* setup bitrate for different rc_mode */
    mpp_enc_cfg_set_s32(m_cfg, "rc:bps_target", m_enc_para.bps);
    switch (m_enc_para.rc_mode) {
    case MPP_ENC_RC_MODE_FIXQP : {
        /* do not setup bitrate on FIXQP mode */
    } break;
    case MPP_ENC_RC_MODE_CBR : {
        /* CBR mode has narrow bound */
        mpp_enc_cfg_set_s32(m_cfg, "rc:bps_max", m_enc_para.bps_max ? m_enc_para.bps_max : m_enc_para.bps * 17 / 16);
        mpp_enc_cfg_set_s32(m_cfg, "rc:bps_min", m_enc_para.bps_min ? m_enc_para.bps_min : m_enc_para.bps * 15 / 16);
    } break;
    case MPP_ENC_RC_MODE_VBR :
    case MPP_ENC_RC_MODE_AVBR : {
        /* VBR mode has wide bound */
        mpp_enc_cfg_set_s32(m_cfg, "rc:bps_max", m_enc_para.bps_max ? m_enc_para.bps_max : m_enc_para.bps * 17 / 16);
        mpp_enc_cfg_set_s32(m_cfg, "rc:bps_min", m_enc_para.bps_min ? m_enc_para.bps_min : m_enc_para.bps * 1 / 16);
    } break;
    default : {
        /* default use CBR mode */
        mpp_enc_cfg_set_s32(m_cfg, "rc:bps_max", m_enc_para.bps_max ? m_enc_para.bps_max : m_enc_para.bps * 17 / 16);
        mpp_enc_cfg_set_s32(m_cfg, "rc:bps_min", m_enc_para.bps_min ? m_enc_para.bps_min : m_enc_para.bps * 15 / 16);
    } break;
    }

    /* setup qp for different codec and rc_mode */
    switch (m_enc_para.type) {
    case MPP_VIDEO_CodingAVC :
    case MPP_VIDEO_CodingHEVC : {
        switch (m_enc_para.rc_mode) {
        case MPP_ENC_RC_MODE_FIXQP : {
            RK_S32 fix_qp = m_enc_para.qp_init;

            mpp_enc_cfg_set_s32(m_cfg, "rc:qp_init", fix_qp);
            mpp_enc_cfg_set_s32(m_cfg, "rc:qp_max", fix_qp);
            mpp_enc_cfg_set_s32(m_cfg, "rc:qp_min", fix_qp);
            mpp_enc_cfg_set_s32(m_cfg, "rc:qp_max_i", fix_qp);
            mpp_enc_cfg_set_s32(m_cfg, "rc:qp_min_i", fix_qp);
            mpp_enc_cfg_set_s32(m_cfg, "rc:qp_ip", 0);
        } break;
        case MPP_ENC_RC_MODE_CBR :
        case MPP_ENC_RC_MODE_VBR :
        case MPP_ENC_RC_MODE_AVBR : {
            mpp_enc_cfg_set_s32(m_cfg, "rc:qp_init", -1);
            mpp_enc_cfg_set_s32(m_cfg, "rc:qp_max", 51);
            mpp_enc_cfg_set_s32(m_cfg, "rc:qp_min", 10);
            mpp_enc_cfg_set_s32(m_cfg, "rc:qp_max_i", 51);
            mpp_enc_cfg_set_s32(m_cfg, "rc:qp_min_i", 10);
            mpp_enc_cfg_set_s32(m_cfg, "rc:qp_ip", 2);
        } break;
        default : {
            log_error("unsupport encoder rc mode %d\n", m_enc_para.rc_mode);
        } break;
        }
    } break;
    case MPP_VIDEO_CodingVP8 : {
        /* vp8 only setup base qp range */
        mpp_enc_cfg_set_s32(m_cfg, "rc:qp_init", 40);
        mpp_enc_cfg_set_s32(m_cfg, "rc:qp_max",  127);
        mpp_enc_cfg_set_s32(m_cfg, "rc:qp_min",  0);
        mpp_enc_cfg_set_s32(m_cfg, "rc:qp_max_i", 127);
        mpp_enc_cfg_set_s32(m_cfg, "rc:qp_min_i", 0);
        mpp_enc_cfg_set_s32(m_cfg, "rc:qp_ip", 6);
    } break;
    case MPP_VIDEO_CodingMJPEG : {
        /* jpeg use special codec config to control qtable */
        mpp_enc_cfg_set_s32(m_cfg, "jpeg:q_factor", 80);
        mpp_enc_cfg_set_s32(m_cfg, "jpeg:qf_max", 99);
        mpp_enc_cfg_set_s32(m_cfg, "jpeg:qf_min", 1);
    } break;
    default : {
    } break;
    }

    /* setup codec  */
    mpp_enc_cfg_set_s32(m_cfg, "codec:type", m_enc_para.type);
    switch (m_enc_para.type) {
    case MPP_VIDEO_CodingAVC : {
        RK_U32 constraint_set = m_enc_para.constraint_set;

        /*
         * H.264 profile_idc parameter
         * 66  - Baseline profile
         * 77  - Main profile
         * 100 - High profile
         */
        mpp_enc_cfg_set_s32(m_cfg, "h264:profile", 100);
        /*
         * H.264 level_idc parameter
         * 10 / 11 / 12 / 13    - qcif@15fps / cif@7.5fps / cif@15fps / cif@30fps
         * 20 / 21 / 22         - cif@30fps / half-D1@@25fps / D1@12.5fps
         * 30 / 31 / 32         - D1@25fps / 720p@30fps / 720p@60fps
         * 40 / 41 / 42         - 1080p@30fps / 1080p@30fps / 1080p@60fps
         * 50 / 51 / 52         - 4K@30fps
         */
        mpp_enc_cfg_set_s32(m_cfg, "h264:level", 40);
        mpp_enc_cfg_set_s32(m_cfg, "h264:cabac_en", 1);
        mpp_enc_cfg_set_s32(m_cfg, "h264:cabac_idc", 0);
        mpp_enc_cfg_set_s32(m_cfg, "h264:trans8x8", 1);

        if (constraint_set & 0x3f0000)
            mpp_enc_cfg_set_s32(m_cfg, "h264:constraint_set", constraint_set);
    } break;
    case MPP_VIDEO_CodingHEVC :
    case MPP_VIDEO_CodingMJPEG :
    case MPP_VIDEO_CodingVP8 : {
    } break;
    default : {
        log_error("unsupport encoder coding type %d\n", m_enc_para.type);
    } break;
    }

    if (m_enc_para.split_mode) {
        log_debug("%p split mode %d arg %d out %d\n", m_mpp_ctx,
                  m_enc_para.split_mode, m_enc_para.split_arg, m_enc_para.split_out);
        mpp_enc_cfg_set_s32(m_cfg, "split:mode", m_enc_para.split_mode);
        mpp_enc_cfg_set_s32(m_cfg, "split:arg", m_enc_para.split_arg);
        mpp_enc_cfg_set_s32(m_cfg, "split:out", m_enc_para.split_out);
    }

    mpp_enc_cfg_set_s32(m_cfg, "prep:mirroring", m_enc_para.mirroring);
    mpp_enc_cfg_set_s32(m_cfg, "prep:rotation", m_enc_para.rotation);
    mpp_enc_cfg_set_s32(m_cfg, "prep:flip", m_enc_para.flip);

    ret = m_mpp_mpi->control(m_mpp_ctx, MPP_ENC_SET_CFG, m_cfg);
    if (ret) {
        log_error("mpi control enc set cfg failed ret %d\n", ret);
        exit(EXIT_FAILURE);
    }

#if 1
    // 关闭5编码器的SEI调试信息输出
    m_enc_para.sei_mode = MPP_ENC_SEI_MODE_DISABLE;
    /* optional */
    {
        ret = m_mpp_mpi->control(m_mpp_ctx, MPP_ENC_SET_SEI_CFG, &m_enc_para.sei_mode);
        if (ret) {
            log_error("mpi control enc set sei cfg failed ret %d", ret);
            exit(EXIT_FAILURE);
        }
    }
#endif

    if (m_enc_para.type == MPP_VIDEO_CodingAVC || m_enc_para.type == MPP_VIDEO_CodingHEVC) {
        m_enc_para.header_mode = MPP_ENC_HEADER_MODE_EACH_IDR;
        ret = m_mpp_mpi->control(m_mpp_ctx, MPP_ENC_SET_HEADER_MODE, &m_enc_para.header_mode);
        if (ret) {
            log_error("mpi control enc set header mode failed ret %d\n", ret);
            exit(EXIT_FAILURE);
        }
    }

#if 0
    RK_U32 gop_mode = m_enc_para.gop_mode;
    if (gop_mode) {
        MppEncRefCfg ref;

        mpp_enc_ref_cfg_init(&ref);

        if (m_enc_para.gop_mode < 4)
            mpi_enc_gen_ref_cfg(ref, gop_mode);
        else
            mpi_enc_gen_smart_gop_ref_cfg(ref, m_enc_para.gop_len, m_enc_para.vi_len);

        ret = mpp_mpi->control(mpp_ctx, MPP_ENC_SET_REF_CFG, ref);
        if (ret) {
            log_error("mpi control enc set ref cfg failed ret %d\n", ret);
            exit(EXIT_FAILURE);
        }
        mpp_enc_ref_cfg_deinit(&ref);
    }

    if (m_enc_para.roi_enable) {
        mpp_enc_roi_init(&m_enc_para.roi_mpp_ctx, m_enc_para.width, m_enc_para.height, m_enc_para.type, 4);
        mpp_assert(m_enc_para.roi_mpp_ctx);
    }
#endif

    return 0;
}

int SmartMppEncoder::asyn_put_enc_frame(std::vector<uint8_t>& data)
{
    return 0;
}

int SmartMppEncoder::loop_asyn_get_enc_packet(std::function<void(uint8_t *, uint32_t)> callback)
{
    return 0;
}

int SmartMppEncoder::get_enc_header(std::vector<uint8_t>& data)
{
    int ret;
    data.clear();

    if (m_enc_para.type == MPP_VIDEO_CodingAVC || m_enc_para.type == MPP_VIDEO_CodingHEVC) {
        MppPacket packet = NULL;

        /*
         * Can use packet with normal malloc buffer as input not pkt_buf.
         * Please refer to vpu_api_legacy.cpp for normal buffer case.
         * Using pkt_buf buffer here is just for simplifing demo.
         */
        mpp_packet_init_with_buffer(&packet, this->m_pkt_buf);
        /* NOTE: It is important to clear output packet length!! */
        mpp_packet_set_length(packet, 0);

        ret = m_mpp_mpi->control(m_mpp_ctx, MPP_ENC_GET_HDR_SYNC, packet);
        if (ret) {
            log_error("mpi control enc get extra info failed\n");
            return -1;
        } else {
            /* get and write sps/pps for H.264 */
            uint8_t* ptr = (uint8_t*)mpp_packet_get_pos(packet);
            size_t len  = mpp_packet_get_length(packet);

            data.insert(data.end(), ptr, ptr+len);
        }

        mpp_packet_deinit(&packet);
    }
    return data.size();
}

int SmartMppEncoder::enc_put_frame_get_packet(uint8_t* p_put_data,uint32_t put_data_len, std::vector<uint8_t>& get_data)
{
    int ret;
    MppMeta meta = NULL;
    MppFrame frame = NULL;
    MppPacket packet = NULL;
    uint8_t* buf = (uint8_t*)mpp_buffer_get_ptr(m_frm_buf);
    get_data.clear();
    
    ret = mpp_frame_init(&frame);
    if (ret) {
        log_error("mpp_frame_init failed\n");
        return -1;
    }
    log_debug("mpp_frame_init seccess.");

    log_debug("new frame width:%d height:%d hor_stride:%d ver_stride:%d fmt:%d.", m_enc_para.width, m_enc_para.height, m_enc_para.hor_stride, m_enc_para.ver_stride, m_enc_para.fmt);
    
    // if (put_data_len >= m_frame_size) {
    //     memcpy(buf, p_put_data, m_frame_size);
    // } else {
    //     log_error("put frame len failed. input len:%d, m_len:%d.", put_data_len, m_frame_size);
    //     return -1;
    // }

    switch (m_enc_para.fmt & MPP_FRAME_FMT_MASK) {
    case MPP_FMT_YUV420SP : break;
    case MPP_FMT_YUV420P : break;
    case MPP_FMT_ARGB8888 :
    case MPP_FMT_ABGR8888 :
    case MPP_FMT_BGRA8888 :
    case MPP_FMT_RGBA8888 :
    case MPP_FMT_RGB101010 :
    case MPP_FMT_BGR101010 : break;
    case MPP_FMT_YUV422P :
    case MPP_FMT_YUV422SP :
    case MPP_FMT_BGR444 :
    case MPP_FMT_RGB444 :
    case MPP_FMT_RGB555 :
    case MPP_FMT_BGR555 :
    case MPP_FMT_RGB565 :
    case MPP_FMT_BGR565 :
    case MPP_FMT_YUV422_YUYV :
    case MPP_FMT_YUV422_YVYU :
    case MPP_FMT_YUV422_UYVY :
    case MPP_FMT_YUV422_VYUY : {
        if (m_enc_para.hor_stride < m_enc_para.width * 2) {
            log_error("invalid %dbit color config: hor_stride %d is smaller then width %d multiply by 4\n", 
                8 * 2, m_enc_para.hor_stride, m_enc_para.width, 2);
            log_error("width  should be defined by pixel count\n");
            log_error("stride should be defined by byte count\n");
            m_enc_para.hor_stride = m_enc_para.width * 2;
        }
        for (int row = 0; row < m_enc_para.height; ++row) {
            memcpy(buf + row * m_enc_para.hor_stride, p_put_data, m_enc_para.width * 2);
            p_put_data += m_enc_para.width * 2;
        }
    } break;
    case MPP_FMT_YUV444SP :
    case MPP_FMT_YUV444P :
    case MPP_FMT_RGB888 :
    case MPP_FMT_BGR888 : break;
    default : {
        log_error("read image do not support fmt %d\n", m_enc_para.fmt);
        ret = MPP_ERR_VALUE;
    } break;
    }   

    mpp_frame_set_width(frame, m_enc_para.width);
    mpp_frame_set_height(frame, m_enc_para.height);
    mpp_frame_set_hor_stride(frame, m_enc_para.hor_stride);
    mpp_frame_set_ver_stride(frame, m_enc_para.ver_stride);
    mpp_frame_set_fmt(frame, m_enc_para.fmt);
    mpp_frame_set_buffer(frame, m_frm_buf);

    meta = mpp_frame_get_meta(frame);
    mpp_packet_init_with_buffer(&packet, m_pkt_buf);

    mpp_packet_set_length(packet, 0);  // NOTE: It is important to clear output packet length
    mpp_meta_set_packet(meta, KEY_OUTPUT_PACKET, packet);
    mpp_meta_set_buffer(meta, KEY_MOTION_INFO, this->m_md_info);
    log_debug("packet buff success.");
    /*
     * NOTE: in non-block mode the frame can be resent.
     * The default input timeout mode is block.
     *
     * User should release the input frame to meet the requirements of
     * resource creator must be the resource destroyer.
     */
    ret = m_mpp_mpi->encode_put_frame(m_mpp_ctx, frame);
    mpp_frame_deinit(&frame);
    if (ret) {
        log_error("encode put frame failed");
        return -1;
    }
    log_debug("encode_put_frame success.");

    ret = m_mpp_mpi->encode_get_packet(m_mpp_ctx, &packet);
    if (ret) {
        log_error("encode get packet failed\n");
        return -1;
    }
    log_debug("encode_get_packet success.");

    if (packet) {
        // write packet to file here
        uint8_t* ptr   = (uint8_t*)mpp_packet_get_pos(packet);
        size_t len  = mpp_packet_get_length(packet);
        char log_buf[256];
        RK_S32 log_size = sizeof(log_buf) - 1;
        RK_S32 log_len = 0;

        /* set encode result */
        get_data.insert(get_data.end(), ptr, ptr+len);


        log_len += snprintf(log_buf + log_len, log_size - log_len,
                            " size %-7zu", len);

        if (mpp_packet_has_meta(packet)) {
            meta = mpp_packet_get_meta(packet);
            RK_S32 temporal_id = 0;
            RK_S32 lt_idx = -1;
            RK_S32 avg_qp = -1;

            if (MPP_OK == mpp_meta_get_s32(meta, KEY_TEMPORAL_ID, &temporal_id))
                log_len += snprintf(log_buf + log_len, log_size - log_len,
                                    " tid %d", temporal_id);

            if (MPP_OK == mpp_meta_get_s32(meta, KEY_LONG_REF_IDX, &lt_idx))
                log_len += snprintf(log_buf + log_len, log_size - log_len,
                                    " lt %d", lt_idx);

            if (MPP_OK == mpp_meta_get_s32(meta, KEY_ENC_AVERAGE_QP, &avg_qp))
                log_len += snprintf(log_buf + log_len, log_size - log_len,
                                    " qp %d.", avg_qp);
        }

        log_debug("encoder %s\n", log_buf);

        mpp_packet_deinit(&packet);
    } else {
        log_debug("encode get packet is empty");
        return -1;
    }

    return 0;
}